package org.tinygame.herostory;

import com.google.protobuf.GeneratedMessageV3;
import com.google.protobuf.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinygame.herostory.msg.GameMsgProtocol;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 消息识别器
 * @author shkstart
 * @create 2022-05-05 20:22
 */
public class GameMsgRecognizer {
    //日志
    private static final Logger LOGGER= LoggerFactory.getLogger(GameMsgRecognizer.class);

    /**
     * 消息编号 和 消息体 字典
     */
    private static final Map<Integer, GeneratedMessageV3> _msgCodeAndMsgBodyMap=new HashMap<>();

    /**
     * 消息类型 和 消息编号 字典
     */
    private static final Map<Class<?>, Integer> _msgClassAndMsgCodeMap=new HashMap<>();
    /**
     * 私有化默认构造器
     */
    private GameMsgRecognizer(){}


    public static void init(){
        LOGGER.info("==== 开始 MsgBody 和 MsgCode 的关联 ====");
        //拿到GameMsgProtocol所有内部类
        Class<?>[] innerClazzArray=GameMsgProtocol.class.getDeclaredClasses();

        //for循环遍历拿到GameMsgProtocol所有内部类，转换成小写
        for(Class<?> innerClazz:innerClazzArray){
            if(!GeneratedMessageV3.class.isAssignableFrom(innerClazz)){
                continue;
            }

            String clazzName=innerClazz.getSimpleName();
            clazzName=clazzName.toLowerCase();

            //for循环遍历GameMsgProtocol.MsgCode中的枚举常量，去掉_并转换成小写
            for(GameMsgProtocol.MsgCode msgCode: GameMsgProtocol.MsgCode.values()){
                String strMsgCode=msgCode.name();//name方法自动去掉了最后了“VALUE”
                strMsgCode=strMsgCode.replaceAll("_","");
                strMsgCode=strMsgCode.toLowerCase();

                if(!strMsgCode.startsWith(clazzName)){
                    continue;
                }
                //如果GameMsgProtocol中的某个内部类
                //和GameMsgProtocol.MsgCode中的某个枚举常量名字匹配
                //则首先获取改内部类的getDefaultInstance
                //并将该内部类、它的getDefaultInstance实例、msgCode三个量放入到上面定义的两个字典中
                try {
                    Object returnObj=innerClazz.getDeclaredMethod("getDefaultInstance").invoke(innerClazz);

                    LOGGER.info("{} <==> {}",innerClazz.getName(),msgCode.getNumber());
                    _msgCodeAndMsgBodyMap.put(msgCode.getNumber(),(GeneratedMessageV3) returnObj);
                    _msgClassAndMsgCodeMap.put(innerClazz,msgCode.getNumber());
                } catch (Exception ex) {
                    LOGGER.error(ex.getMessage(),ex);
                }
                //TODO 由于该次外循环内部类已经匹配到枚举常量，这里是否可以考虑break跳出内部for循环？

            }


        }

    }
    //Decode 通过消息编号判断消息类型
    public static Message.Builder getBuilderByMsgCode(int msgCode){
        if(msgCode<0){
            return null;
        }
        GeneratedMessageV3 msg=_msgCodeAndMsgBodyMap.get(msgCode);
        if(null==msg){
            return null;
        }

        return msg.newBuilderForType();
    }

    //Encode 通过消息类型,得到消息编号
    public static int getMsgCodeByMsgClazz(Class<?> msgClazz){
        if(null==msgClazz){
            return -1;
        }

        Integer msgCode=_msgClassAndMsgCodeMap.get(msgClazz);
        if(null == msgCode){
            return -1;
        }
        return msgCode.intValue();
    }
}
